#include "TcpServer.h"
#include <RsaCrypto.h>
#include <fstream>
#include "Room.h"
#include <unistd.h>
#include "TcpConnection.h"
#include "Log.h"
using namespace std;

// 实例化TcpServer
TcpServer::TcpServer(unsigned short port, int threadNum) : _port(port) {
	_mainEventLoop = new EventLoop;
	_threadNum = threadNum;
	_threadPool = new ThreadPool(_mainEventLoop, _threadNum);
	setListen();
}

// 析构
TcpServer::~TcpServer()
{
}

// 设置监听
void TcpServer::setListen() {
	// 1. 创建监听Fd
	_listenFd = socket(AF_INET, SOCK_STREAM, 0);
	if (_listenFd < 0) {
		LogDebug("socket error!");
		return;
	}

	// 2. 设置端口复用
	int opt = 1;
	int ret = setsockopt(_listenFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
	if (ret < 0) {
		LogDebug("setsockopt error!");
		return;
	}

	// 3. 绑定IP地址
	struct sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_port = htons(_port);
	local.sin_addr.s_addr = INADDR_ANY;
	ret = bind(_listenFd, (struct sockaddr*)&local, sizeof(local));
	if (ret < 0) {
		LogDebug("bind error!");
		return;
	}

	// 4. 设置监听状态
	ret = listen(_listenFd, 128);
	if (ret < 0) {
		LogDebug("listen error!");
		return;
	}
}

// 运行服务器
void TcpServer::run() {
	// 存储Rsa密钥对到Redis服务器
	saveRsaKey();

	// 启动线程池
	_threadPool->run();

	// 添加监视listenFd的任务
	Channel* channel = new Channel(_listenFd, FdEvent::_ReadEvent, acceptConnection, nullptr, nullptr, this);
	_mainEventLoop->addTask(channel, ElemType::ADD);

	// 启动反应堆模型
	LogDebug("server start run...");
	_mainEventLoop->run();
}

// 获取连接
int TcpServer::acceptConnection(void* arg) {
	TcpServer* server = static_cast<TcpServer*>(arg);

	// 1. 获取与客户端建立的连接: 对客户端的网络地址信息不感兴趣
	int connectFd = accept(server->_listenFd, NULL, NULL);
	if (connectFd < 0) {
		LogDebug("accept newlink error!");
		return -1;
	}

	// 2. 从线程池中取出1个子线程的反应堆实例，让其去处理该连接
	EventLoop* evLoop = server->_threadPool->takeWorkerEventLoop();

	// 3. 生成fd对应的TcpConnection对象和channel对象，将fd的TcpConnection注册到fd的channel，并将fd的channel添加到反应堆
	new TcpConnection(connectFd, evLoop);
	return 0;
}

// 存储Rsa密钥对到Redis服务器
void TcpServer::saveRsaKey() {
	// 生成RSA密钥对
	RsaCrypto* rsa = new RsaCrypto();
	rsa->generateRsaKey(RsaCrypto::BITS_2k);
	delete rsa;

	// 读公钥
	ifstream ifs("public.pem");
	stringstream sstr;
	sstr << ifs.rdbuf();
	string data = sstr.str(); // data = 公钥
	ifs.close();

	// 实例化Redis对象
	Room redis;
	assert(redis.initEnvironment());
	redis.clear();
	redis.saveRsaSecKey("PublicKey", data); // 将公钥保存到Redis服务器中

	// 读私钥
	ifs.open("private.pem");
	sstr << ifs.rdbuf();
	data = sstr.str(); // data = 私钥
	ifs.close();
	redis.saveRsaSecKey("PrivateKey", data); // 将私钥保存到Redis服务器中

	// 删除公钥文件和私钥文件(∵不再需要这两个文件了)
	unlink("public.pem");
	unlink("private.pem");
}
